/**
 *Copyright 2013 by dragon.
 *
 *File name: FtpDownloader.java
 *Author:      dragon
 *Email:       fufulove2012@gmail.com
 *Blog:        http://blog.csdn.net/xidomlove
 *Version:     1.0.0
 *Date:        2013-10-5 下午4:54:46
 *Description: 
 */
package com.dragon.jaxel.xtp;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.net.MalformedURLException;
import java.net.Socket;
import java.net.URL;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import com.dragon.log.Logger;

/**
 * @author dragon8
 * 
 */
public class FtpDownloader extends XtpDownloader {

	private String urlString;

	private String userString = "anonymous";

	/**
	 * @param userString
	 *            the userString to set
	 */
	public void setUserString(String userString) {
		this.userString = userString;
	}

	/**
	 * @param passwordString
	 *            the passwordString to set
	 */
	public void setPasswordString(String passwordString) {
		this.passwordString = passwordString;
	}

	private String passwordString = "";

	/**
	 * 
	 */
	public FtpDownloader() {
		// TODO Auto-generated constructor stub
	}

	/**
	 * 
	 */
	public FtpDownloader(String urlString, String savePath) {
		// TODO Auto-generated constructor stub
		this.urlString = urlString;
		super.savePathString = new File(savePath).getAbsolutePath();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.dragon.jsnative.download.Downloader#saveDowanloadState(java.io.
	 * ObjectOutputStream)
	 */
	@Override
	protected void saveDowanloadState(ObjectOutputStream outputStream)
			throws IOException {
		// TODO Auto-generated method stub
		outputStream.writeUTF(urlString);
		outputStream.writeUTF(userString);
		outputStream.writeUTF(passwordString);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.dragon.jsnative.download.Downloader#loadDowanloadState(java.io.
	 * ObjectInputStream)
	 */
	@Override
	protected void loadDowanloadState(ObjectInputStream inputStream)
			throws IOException {
		// TODO Auto-generated method stub
		urlString = inputStream.readUTF();
		userString = inputStream.readUTF();
		passwordString = inputStream.readUTF();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.dragon.jsnative.download.Downloader#getFileLength()
	 */
	@Override
	long getFileLength() {
		// TODO Auto-generated method stub
		try {
			byte[] bytes = new byte[8192];
			URL url = new URL(urlString);
			Socket commandSocket = new Socket(url.getHost(),
					url.getPort() == -1 ? 21 : url.getPort());
			InputStream inputStream = commandSocket.getInputStream();
			OutputStream outputStream = commandSocket.getOutputStream();
			int offset = 0;
			offset += inputStream.read(bytes, offset, bytes.length - offset);
			outputStream.write(String.format("USER %s\r\n", userString)
					.getBytes());
			outputStream.flush();
			offset += inputStream.read(bytes, offset, bytes.length - offset);
			outputStream.write(String.format("PASS %s\r\n", passwordString)
					.getBytes());
			outputStream.flush();
			offset += inputStream.read(bytes, offset, bytes.length - offset);
			outputStream.write(String.format("TYPE I\r\n", "").getBytes());
			outputStream.flush();
			offset += inputStream.read(bytes, offset, bytes.length - offset);
			commandSocket.getOutputStream().write(
					String.format("SIZE %s\r\n", url.getPath()).getBytes());
			outputStream.flush();
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch blockIOException
				Logger.getDefaultLogger().debug(
						"InterruptedException " + e.getMessage());
			}
			offset += inputStream.read(bytes, offset, bytes.length - offset);
			String string = new String(bytes, 0, offset);
			inputStream.close();
			outputStream.close();
			commandSocket.close();
			Pattern pattern = Pattern
					.compile("^213\\s(.+)$", Pattern.MULTILINE);
			Matcher matcher = pattern.matcher(string);
			if (!matcher.find()) {
				Logger.getDefaultLogger().error("FTP get file size failed");
				return -1;
			}
			return Long.valueOf(matcher.group(1));
		} catch (MalformedURLException e) {
			// TODO Auto-generated catch block
			Logger.getDefaultLogger().debug(
					"MalformedURLException " + e.getMessage());
		} catch (IOException e) {
			// TODO Auto-generated catch block
			Logger.getDefaultLogger().debug(
					"InterruptedException " + e.getMessage());
		}
		return -1;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * com.dragon.jsnative.download.Downloader#openConnection(com.dragon.jsnative
	 * .download.ConnectionContext, long, long)
	 */
	@Override
	InputStream openConnection(ConnectionContext context, long bytesBegin,
			long bytesEnd) throws IOException {
		// TODO Auto-generated method stub
		URL url = new URL(urlString);
		byte[] bytes = new byte[8192];
		Context myContext = new Context();
		context.tokenObject = myContext;
		myContext.commandSocket = new Socket(url.getHost(),
				url.getPort() == -1 ? 21 : url.getPort());
		InputStream inputStream = myContext.commandSocket.getInputStream();
		OutputStream outputStream = myContext.commandSocket.getOutputStream();
		int offset = 0;
		offset += inputStream.read(bytes, offset, bytes.length - offset);
		outputStream.write(String.format("USER %s\r\n", userString).getBytes());
		outputStream.flush();
		offset += inputStream.read(bytes, offset, bytes.length - offset);
		outputStream.write(String.format("PASS %s\r\n", passwordString)
				.getBytes());
		outputStream.flush();
		offset += inputStream.read(bytes, offset, bytes.length - offset);
		outputStream.write(String.format("TYPE I\r\n", "").getBytes());
		outputStream.flush();
		offset += inputStream.read(bytes, offset, bytes.length - offset);
		outputStream.write(String.format("PASV\r\n", "").getBytes());
		outputStream.flush();
		try {
			Thread.sleep(100);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			Logger.getDefaultLogger().debug(
					"InterruptedException " + e.getMessage());
		}
		offset += inputStream.read(bytes, offset, bytes.length - offset);
		String string = new String(bytes, 0, offset);
		if (bytesBegin != 0) {
			outputStream.write(String.format("REST %d\r\n", bytesBegin)
					.getBytes());
			outputStream.flush();
			offset += inputStream.read(bytes, offset, bytes.length - offset);
		}

		Pattern pattern = Pattern
				.compile("\\(([0-9]+,[0-9]+,[0-9]+,[0-9]+),([0-9]+),([0-9]+)\\)");

		Matcher matcher = pattern.matcher(string);
		if (matcher.find()) {
			int port = Integer.valueOf(matcher.group(2)) * 256
					+ Integer.valueOf(matcher.group(3));
			myContext.dataSocket = new Socket(matcher.group(1)
					.replace(',', '.'), port);
		} else {
			Logger.getDefaultLogger().error("FTP open data connection failed");
			return null;
		}
		outputStream.write(String.format("RETR %s\r\n", url.getPath())
				.getBytes());
		inputStream.read(bytes);
		return new BufferedInputStream(myContext.dataSocket.getInputStream());
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * com.dragon.jsnative.download.Downloader#closeConnection(com.dragon.jsnative
	 * .download.ConnectionContext)
	 */
	@Override
	protected void closeConnection(ConnectionContext context)
			throws IOException {
		// TODO Auto-generated method stub
		super.closeConnection(context);
		Context myContext = (Context) context.tokenObject;
		if (!myContext.commandSocket.isClosed()) {
			myContext.commandSocket.getOutputStream().close();
			myContext.commandSocket.close();
		}
		if (myContext.dataSocket != null && !myContext.dataSocket.isClosed()) {
			myContext.dataSocket.getOutputStream().close();
			myContext.dataSocket.shutdownInput();
			myContext.dataSocket.getInputStream().close();
			myContext.dataSocket.close();
		}
	}

	class Context {
		Socket commandSocket;
		Socket dataSocket;
	}

	@Override
	public String getProtocol() {
		// TODO Auto-generated method stub
		return "ftp";
	}
}
